package cn.elegent.lock.aspect;

import cn.elegent.lock.annotation.ELegentLock;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;

@Aspect
@Component
@Slf4j
public class ELegentLockAOP {

    @Autowired
    private cn.elegent.lock.ELegentLock eLegentLock;

    /**
     * @annotation 中的路径表示拦截特定注解
     */
    @Pointcut("@annotation(cn.elegent.lock.annotation.ELegentLock)")
    public void lockAop() {
    }

    @Around(value = "lockAop()")
    public Object setElegentLock(ProceedingJoinPoint pjp) throws Throwable {

        // 解析参数
        Method method = resolveMethod(pjp);
        ELegentLock annotation = method.getAnnotation(ELegentLock.class);
        String params = Arrays.toString(pjp.getArgs());
        //获取锁
        String lockName =annotation.lockName()+"_"+params;
        log.info(Thread.currentThread().getName()+"===分布式锁名称{}===",lockName);

        boolean b = eLegentLock.tryLock(lockName);

        if(annotation.always() && b==false){ //如果无法获取锁
            b = eLegentLock.lock(lockName);//努力尝试
        }

        //执行业务
        if(!b){
            throw new IllegalAccessError("当前线程获取分布式锁失败");
        }
        log.info(Thread.currentThread().getName()+"===获取分布式锁{}===",lockName);
        Object ret = pjp.proceed();//执行逻辑
        //释放锁
        eLegentLock.unLock(lockName);
        log.info(Thread.currentThread().getName()+"===释放分布式锁{}===",lockName);
        return ret;

    }

    private Method resolveMethod(ProceedingJoinPoint pjp) throws NoSuchMethodException {
        Signature sig = pjp.getSignature();
        MethodSignature msig = null;
        if (!(sig instanceof MethodSignature)) {
            throw new IllegalArgumentException("该注解只能用于方法");
        }
        msig = (MethodSignature) sig;
        Object target = pjp.getTarget();
        return  target.getClass().getMethod(msig.getName(), msig.getParameterTypes());

    }


}
